﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Slam.Configuration;
using Slam.SharePoint;
using Slam.Logging;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Diagnostics;
using System.Reflection;

namespace Slam.Activation
{
	/// <summary>
	/// The Activator consists of those actions performed by SLAM on its feature activation and deactivation.
	/// 
	/// Activator is the default implementation of SLAM's IActivator.  Activate attaches the SLAM event
	/// handlers to all Lists and Content Types configured to be managed by SLAM in SLAM's configuration.
	/// Deactivate detaches all SLAM event handlers from all configured Lists and Content Types.
	/// </summary>
	public class Activator : IActivator
	{
		#region Private State

		/// <summary>
		/// Fully qualified name of the assembly containing the SLAM event handlers
		/// </summary>
		private string _eventHandlerAssembly = Assembly.GetExecutingAssembly().FullName;

		/// <summary>
		/// Class containing the SLAM event handlers for Content Types
		/// </summary>
		private string _contentTypeEventHandlerClass = "Slam.EventReceivers.ContentTypeEventReceiver";

		/// <summary>
		/// Class containing the SLAM event handlers for Lists
		/// </summary>
		private string _listEventHandlerClass = "Slam.EventReceivers.ListItemEventReceiver";

		/// <summary>
		/// SLAM Configuration
		/// </summary>
		private IConfigurationManager _configuration;

		/// <summary>
		/// SharePoint Services for attaching and detaching event handlers in SharePoint
		/// </summary>
		private SharePointServices _services;

		/// <summary>
		/// Data Synchronizer for Activation
		/// </summary>
		private IDataSynchronizer _synchronizer;

		/// <summary>
		/// Logger for Activation
		/// </summary>
		private ILogger _logger;

		#endregion

		#region Constructors

		/// <summary>
		/// Creates instance of Activator for the given Configuration and DataSynchronizer
		/// </summary>
		/// <param name="configuration"></param>
		/// <param name="synchronizer"></param>
		public Activator(IConfigurationManager configuration, IDataSynchronizer synchronizer, ILogger logger)
		{
			_configuration = configuration;
			_logger = logger;
			_services = new SharePointServices(_logger);
			_synchronizer = synchronizer;
		}

		#endregion

		#region IActivator Members

		/// <summary>
		/// Activates SLAM by attaching all List and Content Type event handlers 
		/// as defined in SLAM's configuration.  Once all event handlers are attached 
		/// the Data Synchronizer is initiated.
		/// </summary>
		/// <returns>Boolean indicating whether Activate succeeded</returns>
		public bool Activate(string siteCollectionUrl)
		{
			using (SPSite siteCollection = new SPSite(siteCollectionUrl))
			{
				string siteCollectionName = siteCollection.ServerRelativeUrl.TrimStart('/');
				TraceLevel currentTraceLevel = TraceLevel.Error;

				//Check if the logger is the default SqlLogger
				SqlLogger sqlLogger = _logger is SqlLogger ? ((SqlLogger)_logger) : null;
				//If it is, force it to log Activation's messages regardless of the
				//configured trace level
				if (sqlLogger != null)
				{
					currentTraceLevel = sqlLogger.Switch.Level;
					sqlLogger.Switch.Level = TraceLevel.Verbose;
				}
				_logger.LogMessage("Information", "Activation Initiated");

				DataMapping currentDataMapping = _configuration.DataMapping;
				if (!String.IsNullOrEmpty(siteCollectionName) && _configuration.DataMappings.ContainsKey(siteCollectionName))
				{
					currentDataMapping = _configuration.DataMappings[siteCollectionName];
				}

				foreach (ConfiguredType type in currentDataMapping.GetAllTypes())
				{
					//If Content Type, attach Content Type handlers.  Otherwise attach List handlers.
					if (type.ContentType)
					{
						_services.AttachContentTypeEventHandlers(siteCollectionUrl, type.Site, type.Name, new Microsoft.SharePoint.SPEventReceiverType[] { Microsoft.SharePoint.SPEventReceiverType.ItemAdding, Microsoft.SharePoint.SPEventReceiverType.ItemUpdating, Microsoft.SharePoint.SPEventReceiverType.ItemAdded, Microsoft.SharePoint.SPEventReceiverType.ItemDeleting, Microsoft.SharePoint.SPEventReceiverType.ItemUpdated }, _eventHandlerAssembly, _contentTypeEventHandlerClass);

						//Add SLAM ID if synchronous or if a TypeIdDefinition is available to this type
						if (!String.IsNullOrEmpty(type.TypeIdDefinition))
						{
							_services.AddSlamIdFieldToContentType(siteCollectionUrl, type.Site, type.Name);
						}

						string typeString = "";
						if (String.IsNullOrEmpty(type.Site))
						{
							typeString = type.Name;
						}
						else
						{
							typeString = String.Format("{0} - {1}", type.Site, type.Name);
						}
						_logger.LogMessage("Information", "Attached Event Handlers for Content Type: " + typeString);
					}
					else
					{
						_services.DetachListEventHandlers(siteCollectionUrl, type.Site, type.Id, type.Name, _eventHandlerAssembly, _contentTypeEventHandlerClass);

						//Check a special case.  If a type includes an "AllSites" attribute value that is set to
						//true event handlers will be attached to ALL Lists with the type Name.  An example of
						//this usage is for slamming Blog Posts.  Blog lists that use the Post Content Type do not
						//allow content types and therefore do not propogate event handlers attached to the Post
						//content type to those Lists that use it.
						if (!String.IsNullOrEmpty(currentDataMapping.GetTypeAttribute(type.Id, type.Name, "AllSites")) && currentDataMapping.GetTypeAttribute(type.Id, type.Name, "AllSites").ToLower() == "true")
						{
							_services.AttachListEventHandler(siteCollectionUrl, "*", type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemAdded, _eventHandlerAssembly, _listEventHandlerClass);
							_services.AttachListEventHandler(siteCollectionUrl, "*", type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemUpdated, _eventHandlerAssembly, _listEventHandlerClass);
							_services.AttachListEventHandler(siteCollectionUrl, "*", type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemDeleting, _eventHandlerAssembly, _listEventHandlerClass);
							_services.AttachListEventHandler(siteCollectionUrl, "*", type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemAdding, _eventHandlerAssembly, _listEventHandlerClass);
							_services.AttachListEventHandler(siteCollectionUrl, "*", type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemUpdated, _eventHandlerAssembly, _listEventHandlerClass);
						}
						else
						{
							_services.AttachListEventHandler(siteCollectionUrl, type.Site, type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemAdded, _eventHandlerAssembly, _listEventHandlerClass);
							_services.AttachListEventHandler(siteCollectionUrl, type.Site, type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemUpdated, _eventHandlerAssembly, _listEventHandlerClass);
							_services.AttachListEventHandler(siteCollectionUrl, type.Site, type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemDeleting, _eventHandlerAssembly, _listEventHandlerClass);
							_services.AttachListEventHandler(siteCollectionUrl, type.Site, type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemAdding, _eventHandlerAssembly, _listEventHandlerClass);
							_services.AttachListEventHandler(siteCollectionUrl, type.Site, type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemUpdating, _eventHandlerAssembly, _listEventHandlerClass);
						}

						//Add SLAM ID if synchronous or if a TypeIdDefinition is available to this type
						if (!String.IsNullOrEmpty(type.TypeIdDefinition))
						{
							_services.AddSlamIdFieldToList(siteCollectionUrl, type.Site, type.Id, type.Name);
						}

						string typeString = "";
						if (String.IsNullOrEmpty(type.Site) && String.IsNullOrEmpty(type.Id))
						{
							typeString = type.Name;
						}
						else if (String.IsNullOrEmpty(type.Site))
						{
							typeString = String.Format("{0} - {1}", type.Id, type.Name);
						}
						else if (String.IsNullOrEmpty(type.Id))
						{
							typeString = String.Format("{0} - {1}", type.Site, type.Name);
						}
						else
						{
							typeString = String.Format("{0} - {1} - {2}", type.Site, type.Id, type.Name);
						}
						_logger.LogMessage("Information", "Attached Event Handlers for List: " + typeString);
					}
				}

				//Synchronize content in SharePoint with SLAM
				_synchronizer.Synchronize(siteCollection.Url);

				_logger.LogMessage("Information", "Activation Complete");

				//If the logger is the default SqlLogger, return it Trace level
				//back to the configured value.
				if (sqlLogger != null)
				{
					sqlLogger.Switch.Level = currentTraceLevel;
				}
			}
			return true;
		}

		/// <summary>
		/// Deactivates SLAM by detaching all List and Content Type event handlers 
		/// associated with SLAM.
		/// </summary>
		/// <returns>Boolean indicating whether Deactivate succeeded</returns>
		public bool Deactivate(string siteCollectionUrl)
		{
			TraceLevel currentTraceLevel = TraceLevel.Error;

			//Check if the logger is the default SqlLogger
			SqlLogger sqlLogger = _logger is SqlLogger ? ((SqlLogger)_logger) : null;
			//If it is, force it to log Activation's messages regardless of the
			//configured trace level
			if (sqlLogger != null)
			{
				currentTraceLevel = sqlLogger.Switch.Level;
				sqlLogger.Switch.Level = TraceLevel.Verbose;
			}
			_logger.LogMessage("Information", "Deactivation Initiated");

			_services.DetachAllEventHandlers(siteCollectionUrl, _eventHandlerAssembly, _listEventHandlerClass, _contentTypeEventHandlerClass);

			_logger.LogMessage("Information", "Deactivation Complete");

			//If the logger is the default SqlLogger, return it Trace level
			//back to the configured value.
			if (sqlLogger != null)
			{
				sqlLogger.Switch.Level = currentTraceLevel;
			}
			
			return true;
		}

		#endregion
	}
}
